package core;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.Map.Entry;

import domain.Table;
import dto.RawSchemaDTO;


public class DeleteTableSequencer {

	private final Map<String, Table> tableMap = new TreeMap<String, Table>();

	private final List<String> tableDeleteOrder = new ArrayList<String>();

	private final SqlEvaluator sqlEvaluator = new SqlEvaluator();

	/**
	 * Gets the result of the run in the order to delete the tables.
	 * @param readFile The raw file to process
	 * @return The list of delete in the of the deletion process
	 */
	public List<String> getResults(final RawSchemaDTO readFile) {
		Iterator<String> iterator = readFile.getRawData().iterator();
		while (iterator.hasNext()) {

			String stmt = iterator.next();

			if (!sqlEvaluator.isComment(stmt)) {

				String fullStmtInOneString = sqlEvaluator.getFullStmtInOneString(stmt, iterator);

				if (sqlEvaluator.isBeginCreateStmt(stmt)) {
					String tableName = noteTableName(fullStmtInOneString);
					noteRelationInCreateStmtIfAny(tableName, stmt);
				}
				else if (sqlEvaluator.isAlterStmt(stmt)) {
					noteRelationshipInAlterStmtIfAny(sqlEvaluator.getTableToAddModify(fullStmtInOneString), fullStmtInOneString);
				}
			}
		}

		determineDeleteProcess();

		return tableDeleteOrder;
	}

	private void noteRelationInCreateStmtIfAny(final String tableName,
			final String stmt) {
		//look for the references keyword
		if (sqlEvaluator.hasReferenceToDependent(stmt)) {
			List<String> dependentTables = sqlEvaluator.getDependentTables(stmt);

			for (String dependentTableName : dependentTables) {
				Table dependentTable = tableMap.get(dependentTableName);
				dependentTable.getChildren().add(tableName);
			}
		}

	}

	private String noteTableName(final String stmt) {
		String tableName = sqlEvaluator.getTableNameFrom(stmt);
		tableMap.put(tableName, new Table(tableName));
		return tableName;
	}

	private void determineDeleteProcess() {
		removeOwnConstraintReferences();

		determineNoParentTables();

		while (!tableMap.isEmpty()) {
			elminateTablesWithChildrenAndPrint();

			//getState(tableMap);
		}
	}

	/**
	 * Removes the foreign key constraints to itself
	 */
	private void removeOwnConstraintReferences() {
		for (Entry<String, Table> entry : tableMap.entrySet()) {
			removeOwnConstraints(entry);
		}
	}

	private void removeOwnConstraints(final Entry<String, Table> entry) {
		String key = entry.getKey();
		List<String> children = entry.getValue().getChildren();

		if (children.contains(key)) {
			children.remove(key);
		}
	}

	private void getState(final Map<String, Table> tableMap) {
		for (Entry<String, Table> entry : tableMap.entrySet()) {
			System.out.println("Table " + entry.getKey() + " has " + entry.getValue());
		}
	}

	private void elminateTablesWithChildrenAndPrint() {
		Set<String> toBeRemoved = new HashSet<String>();

		for (Entry<String, Table> entry : tableMap.entrySet()) {
			if (noDependentsRemain(entry.getValue())) {
				printEntry(entry.getKey());
				toBeRemoved.add(entry.getKey());
			}
		}
		removeTables(toBeRemoved);

		removeDependents();
	}

	private void removeDependents() {
		for (Entry<String, Table> entry : tableMap.entrySet()) {

			Iterator<String> itr = entry.getValue().getChildren().iterator();
			while (itr.hasNext()) {
				String next = itr.next();
				if (!tableMap.containsKey(next)) {
					itr.remove();
				}
			}
		}
	}
	private boolean noDependentsRemain(final Table table) {
		for (String child : table.getChildren()) {
			if (tableMap.containsKey(child)) {
				return false;
			}
		}
		return true;
	}

	private void determineNoParentTables() {
		Set<String> toBeRemoved = new HashSet<String>();

		for (Entry<String, Table> entry : tableMap.entrySet()) {
			if (hasNoDependent(entry)) {
				printEntry(entry.getKey());
				toBeRemoved.add(entry.getKey());
			}
		}

		removeTables(toBeRemoved);
	}

	private void removeTables(final Set<String> toBeRemoved) {
		for (String table : toBeRemoved) {
			tableMap.remove(table);
		}
	}

	private void printEntry(final String key) {
		String deleteStmt = "DELETE FROM " + key + ";";
		System.out.println(deleteStmt);
		tableDeleteOrder.add(deleteStmt);
	}

	private boolean hasNoDependent(final Entry<String, Table> entry) {
		return entry.getValue().getChildren().isEmpty();
	}

	private void noteRelationshipInAlterStmtIfAny(final String tableName, final String stmt) {
		if (sqlEvaluator.hasForeignKey(stmt)) {

			Table table = tableMap.get(sqlEvaluator.getDependentTables(stmt).get(0));
			table.getChildren().add(tableName);
		}
	}

}
